package com.gy.boot.common.dao;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil;
import com.gy.boot.common.util.BlankUtil;
import com.gy.boot.common.util.HttpContextUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 数据权限数据缓存
 * @author geYang
 * */
public class SqlAccess {

    /**
     * 默认缓存长度, 管理员同时在线数量;
     * */
    private static final int online_admin_count = 300;

    /**
     * 用户数据权限缓存, 缓存用户数据权限内容
     * HashMap{用户ID_角色ID: {SQL_ID: AccessRule}}
     * */
    private static final HashMap<String, Map<String, AccessRule>> SQL_ACCESS = MapUtil.newHashMap(online_admin_count);
    /**
     * 获取权限Key
     * @param userId 用户ID
     * @param roleId 角色ID
     * @return 用户ID_角色ID
     * */
    private static String getSQL_ACCESS_Key(Long userId, Integer roleId) {
        return userId + "_" + roleId;
    }
    /**
     * 权限规则类
     * */
    private static class AccessRule {
        private String accessStr; // 权限SQL
        private Set<String> codeSet; // 可控字段数据
        AccessRule(String accessStr, Set<String> codeSet) {
            this.codeSet = codeSet;
            this.accessStr = accessStr;
        }
        String getAccessStr() {
            return accessStr;
        }
        Set<String> getCodeSet() {
            return codeSet;
        }
    }


    /**
     * 设置用户角色权限
     * @param userId 用户ID(当前登录用户)
     * @param roleId 角色ID(当前登录角色)
     * @param listSqlAccess 所有操作权限
     * */
    public static void setSqlAccess(long userId, int roleId, List<IEntity> listSqlAccess) {
        if (BlankUtil.isBlank(listSqlAccess)) {
            return;
        }
        // 数据重组
        Map<String, List<IEntity>> sqlAccessMap = MapUtil.newHashMap(listSqlAccess.size());
        for (IEntity sqlAccess : listSqlAccess) {
            setSqlAccessIEntity(sqlAccess, sqlAccessMap);
        }
        BlankUtil.clean(listSqlAccess);
        Map<String, AccessRule> accessRuleMap = MapUtil.newHashMap(sqlAccessMap.size());
        for (Map.Entry<String, List<IEntity>> sqlAccessEntry : sqlAccessMap.entrySet()) {
            String sql_id = sqlAccessEntry.getKey();
            AccessRule accessRule = getAccessRule(sqlAccessEntry.getValue());
            accessRuleMap.put(sql_id, accessRule);
        }
        String key = getSQL_ACCESS_Key(userId, roleId); // 设置用户角色权限-获取key
        SQL_ACCESS.put(key, accessRuleMap);
    }

    private static AccessRule getAccessRule(List<IEntity> listAccess) {
        Set<String> codeSet = new HashSet<>();
        Map<Integer, String> accessStrMap = new HashMap<>();
        for (IEntity access : listAccess) {
            codeSet.add(access.getStr("code"));
            int level = access.getInt("level");
            String accessStr = getSqlAccessStr(access);
            setAccessStrMap(accessStr, level, accessStrMap);
        }
        StringBuilder accessSB = new StringBuilder();
        for (Map.Entry<Integer, String> accessStrEntry : accessStrMap.entrySet()) {
            accessSB.append("AND (").append(accessStrEntry.getValue()).append(") ");
        }
        String accessStr = accessSB.substring(4);
        return new AccessRule(accessStr, codeSet);
    }

    private static void setAccessStrMap(String accessStr, int level, Map<Integer, String> accessStrMap) {
        if (BlankUtil.isBlank(accessStr)) {
            return;
        }
        String accessStrOld = accessStrMap.get(level);
        if (BlankUtil.isBlank(accessStrOld)) {
            accessStrMap.put(level, accessStr);
            return;
        }
        accessStrMap.put(level, accessStrOld + " OR " + accessStr);
    }

    /**
     * 获取条件SQL
     * */
    private static String getSqlAccessStr(IEntity access) {
        // 数据条件: 等于; 不等于; 小于等于; 大于等于; 包含于; 字符包含; 字符被包含; 为空; 不为空
        String judge = access.getStr("judge");
        // 权限值: 当前用户ID, 当前用户部门ID 等; 当is_fixed=1时也可填写固定值
        String value = access.getStr("value");
        boolean is_fixed = access.getBo("is_fixed"); // 是否固定值
        String code = getCodeStr(access.getStr("code")); // 字段
        if (is_fixed) {
            return getFixedSqlAccessStr(code, value, judge);
        }
        // 非固定值数据
        return getNotFixedSqlAccessStr(code, value, judge);
    }

    /**
     * 获取非固定值条件SQL
     * */
    private static String getNotFixedSqlAccessStr(String code, String value, String judge) {
        String realValue = null; // 真实value
        // 获取当前Value
        if ("当前用户ID".equals(value)) {
            realValue = String.valueOf(HttpContextUtil.getJwtUserId());
        }
        if (BlankUtil.isBlank(realValue)) {
            return null;
        }
        return getFixedSqlAccessStr(code, realValue, judge);
    }

    /**
     * 获取固定值条件SQL
     * */
    private static String getFixedSqlAccessStr(String code, String value, String judge) {
        if ("为空".equals(judge)) {
            return code + " IS NULL";
        }
        if ("不为空".equals(judge)) {
            return code + " IS NOT NULL";
        }
        if ("等于".equals(judge)) {
            return code + " = " + value;
        }
        if ("不等于".equals(judge)) {
            return code + " != " + value;
        }
        if ("小于".equals(judge)) {
            return code + " < " + value;
        }
        if ("小于等于".equals(judge)) {
            return code + " <= " + value;
        }
        if ("大于".equals(judge)) {
            return code + " > " + value;
        }
        if ("大于等于".equals(judge)) {
            return code + " >= " + value;
        }
        if ("包含".equals(judge)) {
            // code 包含 value
            return "FIND_IN_SET('"+value+"', '"+code+"') > 0";
        }
        if ("包含于".equals(judge)) {
            // code 包含于 value
            return code + "IN ("+value+")";
        }
        // code 字符包含 value
        if ("字符包含".equals(judge)) {
            // value属于code (code > value);
            return "INSTR('"+code+"', "+value+") > 0";
        }
        // code 字符被包含 value
        if ("字符被包含".equals(judge)) {
            // code属于value (value > code);
            return "INSTR('"+value+"', "+code+") > 0";
        }
        return null;
    }


    private static String getCodeStr(String code) {
        return "${"+code+"}";
    }

    /**
     * 重组数据
     * */
    private static void setSqlAccessIEntity(IEntity sqlAccess, Map<String, List<IEntity>> sqlAccessMap) {
        String sql_id = sqlAccess.getStr("sql_id");
        List<IEntity> listAccess = sqlAccessMap.get(sql_id);
        if (listAccess == null) {
            listAccess = new ArrayList<>();
        }
        listAccess.add(sqlAccess);
        sqlAccessMap.put(sql_id, listAccess);
    }

    /**
     * 默认获取当前用户数据权限Key
     * */
    private static String getSqlAccessKey() {
        return getSQL_ACCESS_Key(HttpContextUtil.getJwtUserId(), HttpContextUtil.getJwtRoleId());
    }

    /**
     * 清除数据权限
     * */
    public static void clearDataAccess() {
        SQL_ACCESS.remove(getSqlAccessKey());
    }

    private static AccessRule getAccessRule(String sqlID) {
        String sqlAccessKey = getSqlAccessKey();
        Map<String, AccessRule> accessRuleMap = SQL_ACCESS.get(sqlAccessKey);
        if (BlankUtil.isBlank(accessRuleMap)) {
            return null;
        }
        return accessRuleMap.get(sqlID);
    }

    /**
     * 获取权限SQL
     * */
    static String getAccessSql(String sqlID, String sqlStr) {
        boolean isSqlNull = BlankUtil.isBlank(sqlID) || BlankUtil.isBlank(sqlStr);
        if (isSqlNull) {
            return null;
        }
        // 获取权限条件
        AccessRule accessRule = getAccessRule(sqlID);
        if (accessRule == null) {
            return null;
        }
        String accessSqlStr = accessRule.getAccessStr();
        Set<String> codeSet = accessRule.getCodeSet();
        if (BlankUtil.isBlank(accessSqlStr) || BlankUtil.isBlank(codeSet)) {
            return null;
        }
        // 1. 获取权限列
        List<String> listField = MySqlParser.listField(sqlStr);
        System.out.println(listField);
        for (String code : codeSet) {
            String fieldName = MySqlParser.getFieldName(code, listField);
            String codeStr = ReUtil.escape(getCodeStr(code));
            accessSqlStr = accessSqlStr.replaceAll(codeStr, fieldName);
        }
        System.out.println("权限SQL: " + accessSqlStr);
        return accessSqlStr;
    }

    /**
     * 绑定 WHERE SQL权限
     * */
    static String addAccessSql(String oldSql, String accessSql) {
        if (BlankUtil.isBlank(oldSql)) {
            return null;
        }
        if (BlankUtil.isBlank(accessSql)) {
            return oldSql;
        }
        return MySqlParser.addWhereCondition(oldSql, accessSql); // 添加权限条件
    }

}
